/**
 * 
 */
package com.dalonedrow.module.hq.ui.widgets;

import com.dalonedrow.engine.animation.AnimationSequenceObjectFactory;
import com.dalonedrow.engine.sprite.base.SimpleDimension;
import com.dalonedrow.engine.sprite.base.SimpleVector3;
import com.dalonedrow.engine.sprite.base.SpriteBase;
import com.dalonedrow.engine.sprite.base.SpriteImageObject;
import com.dalonedrow.engine.sprite.base.SpriteImageObjectFactory;
import com.dalonedrow.engine.sprite.impl.BasicSpriteObject;
import com.dalonedrow.engine.systems.JOGLErrorHandler;
import com.dalonedrow.engine.systems.Render;
import com.dalonedrow.engine.systems.Time;
import com.dalonedrow.engine.systems.base.Animation;
import com.dalonedrow.engine.systems.base.GameState;
import com.dalonedrow.engine.systems.base.Interactive;
import com.dalonedrow.engine.ui.base.panel.PortraitPagePanel;
import com.dalonedrow.globalvariables.constants.hq.GlobalConstants;
import com.dalonedrow.module.hq.rpg.HQInteractiveObject;
import com.dalonedrow.module.hq.rpg.HQInventoryData;
import com.dalonedrow.module.hq.systems.HQController;
import com.dalonedrow.pooled.PooledStringBuilder;
import com.dalonedrow.pooled.StringBuilderPool;
import com.dalonedrow.rpg.base.flyweights.BaseInteractiveObject;

/**
 * @author Donald
 */
@SuppressWarnings("rawtypes")
public final class HQSpellPanel extends PortraitPagePanel {
	/** the one and only instance of the <code>GameController</code> class. */
	private static HQSpellPanel	instance;
	/**
	 * Gives access to the singleton instance of {@link HQSpellPanel}.
	 * @return {@link HQSpellPanel}
	 */
	public static HQSpellPanel getInstance() {
		if (HQSpellPanel.instance == null) {
			HQSpellPanel.instance = new HQSpellPanel();
		}
		return HQSpellPanel.instance;
	}
	/** the GlobalConstants highlight. */
	private BaseInteractiveObject	highlight;
	/** the current player whose spells are being viewed. */
	private HQInteractiveObject		player;
	/** the spell icons. */
	private SpriteBase[]			spellIcons;
	/** the veils displayed when a spell is unavailable. */
	private SpriteBase[]			spellVeils;
	/** the preferred tooltip position. */
	private SimpleVector3					tooltipPosition;
	/** the spell slot backgrounds. */
	private SpriteBase[]			spellFrames;
	/**
	 * Creates a new instance of {@link HQSpellPanel}.
	 */
	private HQSpellPanel() {
		super.setHandleMouseOverIndependent(true);
		// initialize the view
		// set the border
		// super.setBorder(new HQMonoInventoryBorder());
		int myCols = GlobalConstants.NUM_SPELLS / GlobalConstants.SPELL_ICON_ROWS;
		int myWidth = myCols * GlobalConstants.IMG_SIZE;
		// padding between each column
		myWidth += (myCols - 1) * slotPadding;
		if (super.getBorder() != null) {
			myWidth += super.getBorder().getInsets().left;
			myWidth += super.getBorder().getInsets().right;
		}
		int myHeight = GlobalConstants.SPELL_ICON_ROWS * GlobalConstants.IMG_SIZE;
		// padding between each row
		myHeight += (GlobalConstants.INV_ROWS - 1) * slotPadding;
		if (super.getBorder() != null) {
			myHeight += super.getBorder().getInsets().top;
			myHeight += super.getBorder().getInsets().bottom;
		}
		super.setPreferredSize(myWidth, myHeight);
		spellIcons = new SpriteBase[GlobalConstants.NUM_SPELLS];
		spellVeils = new SpriteBase[GlobalConstants.NUM_SPELLS];
		spellFrames = new SpriteBase[GlobalConstants.NUM_SPELLS];
		try {
			for (int i = 0; i < spellIcons.length; i++) {
				spellFrames[i] = new BasicSpriteObject();
				SpriteImageObject img =
						SpriteImageObjectFactory.getInstance().getImageByName(
								"hq_ultima_inv_panel_unequipped_bkg");
				spellFrames[i].setImageRefId(img.getRefId());
				spellFrames[i].setWidth(img.getWidth());
				spellFrames[i].setHeight(img.getHeight());
				
				spellIcons[i] = new BasicSpriteObject();
				img = SpriteImageObjectFactory.getInstance().getImageByName(
								GlobalConstants.SPELL_ICONS[i]);
				spellIcons[i].setImageRefId(img.getRefId());
				spellIcons[i].setWidth(img.getWidth());
				spellIcons[i].setHeight(img.getHeight());
				
				spellVeils[i] = new BasicSpriteObject();
				img = SpriteImageObjectFactory.getInstance().getImageByName(
								"hq_ultima_spell_inactive_overlay");
				spellVeils[i].setImageRefId(img.getRefId());
				spellVeils[i].setWidth(img.getWidth());
				spellVeils[i].setHeight(img.getHeight());
			}
			highlight =
					(BaseInteractiveObject) Interactive.getInstance()
							.getNewIO();
			SpriteBase sb = new BasicSpriteObject();
			SpriteImageObject img =
					SpriteImageObjectFactory.getInstance().getImageByName(
							"hq_ultima_inv_sel_cursor_0");
			sb.setImageRefId(img.getRefId());
			sb.setWidth(img.getWidth());
			sb.setHeight(img.getHeight());
			highlight.setSprite(sb);
			highlight.addAnimation("selection",
					AnimationSequenceObjectFactory.getInstance()
							.getSequenceRefId("hq_ultima_inv_sel_cursor"));
		} catch (Exception ex) {
			JOGLErrorHandler.getInstance().fatalError(ex);
		}
	}
	/** Clears the highlight GlobalConstants. */
	public void clearHighlight() {
		if (highlight != null) {
			Animation.getInstance().stopAllAnimations(highlight);
			highlight.getSprite().setPosition(null);
		}
	}
	/**
	 * Gets a description for a specific spell.
	 * @param spell the spell id
	 * @return {@link String}
	 * @throws Exception if an error occurs
	 */
	private String getDescriptionString(final int spell)
			throws Exception {
		PooledStringBuilder sb = StringBuilderPool.getInstance()
				.getStringBuilder();
		switch (spell) {
		case GlobalConstants.SPELL_00_AIR_SWIFT_WIND:
			sb.append("SWIFT WIND\n");
			sb.append("Air Spell\n");
			sb.append("A powerful burst of energy allows the hero to move ");
			sb.append("twice as far next turn.\n");
			break;
		case GlobalConstants.SPELL_01_EARTH_HEAL_BODY:
			sb.append("Heal Body\n");
			sb.append("Earth Spell\n");
			sb.append("Up to 4 lost Body Points are immediately restored.\n");
			break;
		case GlobalConstants.SPELL_02_FIRE_FIRE_OF_WRATH:
			sb.append("Fire of Wrath\n");
			sb.append("Fire Spell\n");
			sb.append("Blasts one enemy into flames for 1 Body Point of ");
			sb.append("damage.\n");
			break;
		case GlobalConstants.SPELL_03_WATER_WATER_OF_HEALING:
			sb.append("Water of Healing\n");
			sb.append("Water Spell\n");
			sb.append("Up to 4 lost Body Points are immediately restored.\n");
			break;
		case GlobalConstants.SPELL_04_AIR_TEMPEST:
			sb.append("Tempest\n");
			sb.append("Air Spell\n");
			sb.append("A small whirlwind envelops your enemy and causes them ");
			sb.append("to lose their next turn.\n");
			break;
		case GlobalConstants.SPELL_05_EARTH_ROCK_SKIN:
			sb.append("Rock Skin\n");
			sb.append("Earth Spell\n");
			sb.append("Defense is increased by 1 point. The spell is broken ");
			sb.append("when you suffer any damage.\n");
			break;
		case GlobalConstants.SPELL_06_FIRE_COURAGE:
			sb.append("Courage\n");
			sb.append("Fire Spell\n");
			sb.append("While in the presence of enemies, you have 2 extra ");
			sb.append("Attack points. The spell is broken when you can no ");
			sb.append("longer see any enemies.\n");
			break;
		case GlobalConstants.SPELL_07_WATER_SLEEP:
			sb.append("Sleep\n");
			sb.append("Water Spell\n");
			sb.append("Puts 1 enemy into a deep sleep so it cannot move, ");
			sb.append("attack, or defend itself. May not be used against ");
			sb.append("Mummies, Zombies, or Skeletons.\n");
			break;
		case GlobalConstants.SPELL_08_AIR_GENIE:
			sb.append("Genie\n");
			sb.append("Air Spell\n");
			sb.append("Conjures up a Genie who will do one of the following: ");
			sb.append("open any door or launch a powerful attack against 1 ");
			sb.append("monster within sight.\n");
			break;
		case GlobalConstants.SPELL_09_EARTH_PASS_THROUGH_ROCK:
			sb.append("Pass Through Rock\n");
			sb.append("Earth Spell\n");
			sb.append("You make pass through walls on your next move. ");
			sb.append("Caution! If you end your move in solid rock you ");
			sb.append("become trapped forever!.\n");
			break;
		case GlobalConstants.SPELL_10_FIRE_BALL_OF_FLAME:
			sb.append("Ball of Flame\n");
			sb.append("Fire Spell\n");
			sb.append("Envelops one enemy in a ball of fire	for 2 Body ");
			sb.append("Points of damage.\n");
			break;
		default:
			sb.append("Veil of Mist\n");
			sb.append("Water Spell\n");
			sb.append("On your next move, you may move unseen through spaces ");
			sb.append("that are occupied by monsters.\n");
			break;
		}
		String t = sb.toString();
		sb.returnToPool();
		return t;
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public SimpleDimension getPreferredSizeWhenDisplayingTooltip() {
		return new SimpleDimension(GlobalConstants.IMG_SIZE, GlobalConstants.IMG_SIZE);
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public SimpleVector3 getPreferredTooltipPosition() {
		SimpleVector3 pos = tooltipPosition;
		if (pos == null) {
			pos = super.getPosition();
		}
		return pos;
	}
	/**
	 * Gets the screen position of a specific inventory slot.
	 * @param index the inventory slot
	 * @param zMod any modification the the z-axis applied
	 * @return {@link SimpleVector3}
	 */
	public SimpleVector3 getSlotPosition(final int index, final float zMod) {
		int myCols = GlobalConstants.NUM_SPELLS / GlobalConstants.SPELL_ICON_ROWS;
		// x starts out at the left-side of the widget
		float x = (float) super.getPosition().getX();
		if (super.getBorder() != null) {
			x += super.getBorder().getInsets().left;
		}
		float y = (float) super.getPosition().getY();
		if (super.getBorder() != null) {
			y += super.getBorder().getInsets().bottom;
		}
		float z = (float) super.getPosition().getZ();
		z += (float) GlobalConstants.CHILD_STEP * zMod;
		// find out column of index
		float colNum = index % myCols;
		x += colNum * GlobalConstants.IMG_SIZE;
		x += colNum * slotPadding;

		y += GlobalConstants.IMG_SIZE * (GlobalConstants.SPELL_ICON_ROWS - 1);
		y += slotPadding * (GlobalConstants.SPELL_ICON_ROWS - 1);
		
		// find out row number
		int rowNum = index / myCols;
		y -= rowNum * GlobalConstants.IMG_SIZE;
		y -= slotPadding * rowNum;
		return new SimpleVector3(x, y, z);
	}
	/**
	 * Gets the id of the spell being hovered at a specific location.
	 * @param x the x-position
	 * @param y the y-position
	 * @return <code>int</code>
	 */
	public int getSpellId(final float x, final float y) {
		int spellId = -1;
		if (player != null
				&& player.hasIOFlag(GlobalConstants.IO_01_PC)) {
			HQInventoryData inventory = player.getInventory();
			if (inventory != null) {
				for (int i = 0; i < inventory.getNumInventorySlots(); i++) {
					SimpleVector3 slot = getSlotPosition(i, 0);
					if (slotContains(slot.getX(), slot.getY(), x, y)) {
						spellId = i;
						break;
					}
				}
			}
		}
		return spellId;
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean touchUp(int x, int y, int pointer, int button) {
		int spellId = getSpellId(x, y);
		if (GameState.getInstance().getCurrentState() 
				!= GameState.STATE_14HQ_CHAR_CHOOSER
				&& spellId > -1) {
			clearHighlight();
			// SpellDialog.getInstance().showDialog(
			// getInventorySlotForPosition(x, y));
		}
		return true;
	}
	/** the padding between inventory slots. */
	private final int slotPadding = 1;
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void handleMouseOver(final float x, final float y) {
		int spellId = getSpellId(x, y);
		if (spellId > -1) {
			// play selection GlobalConstants
			try {
				if (GameState.getInstance().getCurrentState() 
						!= GameState.STATE_14HQ_CHAR_CHOOSER) {
					HQController.getInstance().setCursor(GlobalConstants.CURSOR_12_HAND);
				} else {
					HQController.getInstance().setCursor(GlobalConstants.CURSOR_00_DEFAULT);
				}
				String ttText = getDescriptionString(spellId);
				int slot = getSpellId(x, y);
				tooltipPosition = getSlotPosition(slot, 0);
				highlight.getSprite().setPosition(getSlotPosition(
						slot, GlobalConstants.INT_003));
				Animation.getInstance().playAnimation(highlight,
						highlight.getAnimation("selection"));

				// set tooltip
				if (ttText.equalsIgnoreCase(super.getTooltipText())) {
					// already started tooltip for this item
					if (super.hasTooltipTimerStarted()) {
						// check to see if it's time
						// to display the tooltip
						super.updateTooltipTimer(
								Time.getInstance().getGameTime());
					} else { // start the tooltip timer
						super.startTooltipTimer(
								Time.getInstance().getGameTime());
					}
				} else {
					// set new tooltip text
					super.setTooltipText(ttText);
					// stop previous tooltips
					if (super.hasTooltipTimerStarted()) {
						// panel has tooltip - stop them
						super.stopTooltipTimer();
					}
					// start tooltip for this item
					super.startTooltipTimer(Time.getInstance().getGameTime());
				}
			} catch (Exception ex) {
				JOGLErrorHandler.getInstance().fatalError(ex);
			}
		} else {
			Animation.getInstance().stopAllAnimations(highlight);
			highlight.getSprite().setPosition(null);
			tooltipPosition = null;
			super.setTooltipText(null);
			if (super.hasTooltipTimerStarted()) {
				// panel has tooltip - stop them
				super.stopTooltipTimer();
			}
			HQController.getInstance().setCursor(GlobalConstants.CURSOR_00_DEFAULT);
		}
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void prepareForRendering() throws Exception {
		super.prepareForRendering();
		if (super.isVisible()) {
			// check inventory slots
			if (player != null) {
				for (int i = 0; i < GlobalConstants.NUM_SPELLS; i++) {
					// draw the spell icon
					spellIcons[i].setPosition(getSlotPosition(i, 1));
					Render.getInstance().registerEntity(spellIcons[i]);
					// draw the spell frame
					spellFrames[i].setPosition(getSlotPosition(i, 2));
					Render.getInstance().registerEntity(spellFrames[i]);
					// draw spell veil
					if (!player.getPCData().hasSpellsAvailable(
							GlobalConstants.SPELL_BIT_FLAGS[i])) {
						// draw greyed-out
						spellVeils[i].setPosition(
								getSlotPosition(i, GlobalConstants.INT_003));
						Render.getInstance().registerEntity(spellVeils[i]);
					}
				}
				// draw highlight
				if (highlight.getSprite().getPosition() != null) {
					Render.getInstance().registerEntity(
							highlight.getSprite());
				}
			}
		}
	}
	/**
	 * Sets the player instance being viewed.
	 * @param playerIO {@link HQInteractiveObject} the player instance
	 */
	public void setPlayerViewed(final HQInteractiveObject playerIO) {
		player = playerIO;
	}
	/**
	 * Determines if the slot contains a specific point.
	 * @param x0 the slot's left side
	 * @param y0 the slot's bottom side
	 * @param x1 the point's x-position
	 * @param y1 the point's y-position
	 * @return true if the slot contains the point; false otherwise
	 */
	private boolean slotContains(final double x0, final double y0,
			final double x1, final double y1) {
		return x1 >= x0
				&& y1 >= y0
				&& x1 < x0 + GlobalConstants.IMG_SIZE
				&& y1 < y0 + GlobalConstants.IMG_SIZE;
	}
}
